using System;
using System.Collections;
using System.Reflection;
using System.IO;  // for Path
using Meta;

namespace Meta
{
	[AttributeUsage(AttributeTargets.Assembly)]
	public class LoaderClass : Attribute {
		string typeName;
		public string TypeName {
			get {
				return typeName;
			}
		}

		public LoaderClass(string typeName) {
			this.typeName = typeName;
		}
	}

	public class Loader {
		static Loader() {
			AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
		}

		public static NamespaceBase ResolveNamespace(string name) {
			switch (name) {
				case "Meta.RootNamespace":
					return Namespace.RootNamespace;

				case "Meta.UserNamespace":
					return Namespace.UserNamespace;

				default:
					if (name.StartsWith("Meta.Packages") || name.StartsWith("Meta.Examples."))
						return Package.FindPackageFromCLRName(name);
					else
						return NativeNamespace.Find(name);
			}
		}

		static Hashtable loadedNamespacesTable = new Hashtable();
		static Namespace FindScratchNamespace(string name) {
			object probe = loadedNamespacesTable[name];
			if (probe != null)
				return (Namespace)probe;
			Namespace n = new Namespace(name);
			//n.CLRNamespace = name;
			loadedNamespacesTable[name] = n;
			return n;
		}

		public static GlobalVariable ResolveGlobal(string namespaceName, string variableName) {
			return ResolveGlobal(ResolveNamespace(namespaceName), variableName);
		}

		public static GlobalVariable ResolveGlobal(NamespaceBase n, string variableName) {
			return n.Lookup(Symbol.Intern(variableName), false, true, true);
		}

		public static string FindLoadFile(string sourcePath) {
			string realLoadPath;
			if (Meta.TopLevel.CurrentSourceFileToken==null || Meta.TopLevel.CurrentSourceFileToken.Path==null)
				realLoadPath = Path.GetFullPath(sourcePath);
			else
				realLoadPath = Path.Combine(Path.GetDirectoryName(Meta.TopLevel.CurrentSourceFileToken.Path), sourcePath);
			if (Path.GetExtension(realLoadPath)=="") {
				string probe = Path.ChangeExtension(realLoadPath, ".dll");
				if (File.Exists(probe))
					realLoadPath = probe;
				else
					realLoadPath = Path.ChangeExtension(realLoadPath, ".meta");
			}
			return realLoadPath;
		}

		private static Assembly ResolveAssembly(object sender, ResolveEventArgs e) {
			string aName = e.Name.Substring(0, e.Name.IndexOf(','));
			if (aName=="Meta.resources")
				return null;

			AppDomain d = (AppDomain)sender;
			foreach (Assembly a in d.GetAssemblies()) {
				foreach (AssemblyName n in a.GetReferencedAssemblies()) {
					if (n.Name==aName) {
						// Found an Assembly that references it.  Try its directory.
						string probedir = Path.GetDirectoryName(a.Location);
						string probe = Path.Combine(probedir, aName+".dll");
						if (File.Exists(probe))
							return Assembly.LoadFile(probe);
					}
				}
			}
			// Failed to find the assembly that references it and/or couldn't find an assembly of the right name in the same directory.
			return null;
		}

		public static void Load(string name) {
			Load(Assembly.LoadFile(name));
		}
		public static bool Load(Assembly a) {
			Type init=null;
			object[] t = a.GetCustomAttributes(typeof(LoaderClass), true);

			if (t!=null && t.Length>0) {
				string typeName = ((LoaderClass)t[0]).TypeName;
				init = a.GetType(typeName);
				if (init==null)
					throw new Exception("Couldn't find loader class "+typeName);
			} 
			
#if OLDINITIALIZERS
			else {
				init = a.GetType("Initializer");
			}
#endif

			if (init!=null) {
				MethodInfo m = init.GetMethod("Load");
				if (m!=null) {
					m.Invoke(null, new object[0]);
					return true;
				}
			}
			return false;
		}
	}
}
